using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using Mud.Common;
using Mud.Common.Communication;
using Mud.Common.Messages;
using Mud.Common.Threading;


using Mud.Server.Users;
using Mud.Server.Physical;


namespace Mud.Server.Client
{
    /// <summary>
    /// Represents a client which is working asynchronously.
    /// </summary>
    public class AsyncClientBase : ServerObject, IClient
    {

        #region IServerClient members

        /// <summary>
        /// Queue where the event messages are stored.
        /// This queue is processed with ProcessEventQueue method and the events
        /// stored there are processed.
        /// </summary>
        Queue<IMessage> IClient.EventQueue
        {
            get { return _eventQueue; }
        }
        private Queue<IMessage> _eventQueue = new Queue<IMessage>();

        /// <summary>
        /// Status of this client.
        /// </summary>
        ClientStatus IClient.Status
        {
            get { return _status; }
            set { _status = value; }
        }
        private ClientStatus _status;

        /// <summary>
        /// Gets the date when the client was created.
        /// </summary>
        DateTime IClient.DateCreated
        {
            get { return _dateCreated; }
        }
        private DateTime _dateCreated = DateTime.Now;

        /// <summary>
        /// User that this client represents.
        /// </summary>
        /// <value></value>
        public User User
        {
            get { return _user; }
        }
        private User _user;


		public ClientCreatureControler CreatureControler {
			get { return _controler; }
		}
		private ClientCreatureControler _controler;


        /// <summary>
        /// Processes the command.
        /// </summary>
        bool IClient.ProcessCommand()
        {
            IMessage commandMessage;
            lock (this)
            {
                if (this.SynchronizedCommand == null)
                    return false;
                commandMessage = this.SynchronizedCommand;
                this.SynchronizedCommand = null;
            }
            Logger.Log(LogType.Info, "AsyncClientBase.ProcessCommand", "Client {0} ({1}) sent command {2}.", this.ObjectId, this.Name, commandMessage);
			Client.Commands.ICommand command = this.Server.CurrentCommandFactory.CreateHandler(commandMessage);
            IMessage commandResponseMessage;
            if (command == null)
            {
                commandResponseMessage = new MessageResponseError();
                ((MessageResponseError)commandResponseMessage).Description = "Unknown command.";
            }
            else
            {
                try
                {
                    commandResponseMessage = command.Execute(this.Server, commandMessage, this);
                }
                catch (Exception ex)
                {
                    Logger.Log(LogType.Error, "AsyncClientBase.ProcessCommand", "There was an error while executing the command {0} from client {1} ({2}). Error {3}.", commandMessage, this.ObjectId, this.Name, ex);
                    commandResponseMessage = new MessageResponseError();
                    ((MessageResponseError)commandResponseMessage).Description = "There was an error while executing the command.";
                }
            }
            Logger.Log(LogType.Info, "AsyncClientBase.ProcessCommand", "Response to command {0} is {1}.", commandMessage, commandResponseMessage);
            lock (this)
            {
                this.SynchronizedCommandResponse = commandResponseMessage;
            }
            return true;
        }

        /// <summary>
        /// Processes the event queue.
        /// </summary>
        /// <remarks>
        /// This method actualy pushes the event queue to the event list for transfer to
        /// the remote client.
        /// </remarks>
        int IClient.ProcessEventQueue()
        {
            lock (this)
            {
                int eventCount = ((IClient)this).EventQueue.Count;
                foreach (IMessage msg in ((IClient)this).EventQueue)
                    Logger.Log(LogType.ExtraDebug, "AsyncClientBase.ProcessEventQueue", "Client {0} ({1}) received event {2}.", this.ObjectId, this.Name, msg);

                this._eventList.AddRange(((IClient)this).EventQueue);
                ((IClient)this).EventQueue.Clear();
                return eventCount;
            }
        }

        /// <summary>
        /// Called by the server when the client should be disconnected or is already disconnected.
        /// </summary>
        void IClient.DisconnectClient()
        {
            MessageEventClientDisconnected msg = new MessageEventClientDisconnected();
            msg.ClientName = this.User.UserName;
            msg.ClientObjectId = this.ObjectId;

            lock (this)
            {
                this.SynchronizedEventList.Add(msg);
            }

            Server.ClientConnected -= new EventHandler<ClientConnectedEventArgs>(Server_ClientConnected);
            Server.ClientDisconnected -= new EventHandler<ClientDisconnectedEventArgs>(Server_ClientDisconnected);
            Server.ServerStopping -= new EventHandler(Server_ServerStopping);
        }

        #endregion

        #region Server event handlers

        void Server_ClientConnected(object sender, ClientConnectedEventArgs e)
        {
            MessageEventClientConnected msg = new MessageEventClientConnected();
            msg.ClientName = e.Client.Name;
            msg.ClientObjectId = e.Client.ObjectId;
            ((IClient)this).EventQueue.Enqueue(msg);
        }

        void Server_ClientDisconnected(object sender, ClientDisconnectedEventArgs e)
        {
            MessageEventClientDisconnected msg = new MessageEventClientDisconnected();
            msg.ClientName = e.Client.Name;
            msg.ClientObjectId = e.Client.ObjectId;
            ((IClient)this).EventQueue.Enqueue(msg);
        }

        void Server_ServerStopping(object sender, EventArgs e)
        {
            MessageEventServerStopping msg = new MessageEventServerStopping();
            ((IClient)this).EventQueue.Enqueue(msg);
        }

        #endregion

        #region Synchronization

        /// <summary>
        /// When the receiving thread receives the command it is placed in this property.
        /// </summary>
        public IMessage SynchronizedCommand
        {
            get { return _synchedCommand; }
            set { _synchedCommand = value; }
        }
        private IMessage _synchedCommand;

        /// <summary>
        /// When we want to send a response to the command then we place it into this property.
        /// </summary>
        public IMessage SynchronizedCommandResponse
        {
            get { return _synchedCommandResponse; }
            set
            {
                if (_synchedCommandResponse != value)
                {
                    _synchedCommandResponse = value;
                    if (_synchedCommandResponse != null)
                        _commandResponseExists.Set();
                    else
                        _commandResponseExists.Reset();
                }
            }
        }
        private IMessage _synchedCommandResponse;

        /// <summary>
        /// Object can wait on this WaitHandle until response arrives for the command.
        /// </summary>
        public WaitHandle CommandResponseExists
        {
            get { return _commandResponseExists; }
        }
        private ManualResetEvent _commandResponseExists = new ManualResetEvent(false);


        /// <summary>
        /// Holds the list of messages which will be sent to the remote client.
        /// </summary>
        public WaitableList<IMessage> SynchronizedEventList
        {
            get { return _eventList; }
        }
        private WaitableList<IMessage> _eventList = new WaitableList<IMessage>();


        #endregion


        /// <summary>
		/// Initializes a new instance of the <see cref="T:AsyncClientBase"/> class.
        /// </summary>
        /// <param name="server">Server.</param>
		public AsyncClientBase(IServer server, User user)
			: base(server) 
        {
            if (user == null)
                throw new ArgumentNullException("user");
            _user = user;
			_controler = new ClientCreatureControler(this);
            this.Name = user.UserName;
            Server.ClientConnected += new EventHandler<ClientConnectedEventArgs>(Server_ClientConnected);
            Server.ClientDisconnected += new EventHandler<ClientDisconnectedEventArgs>(Server_ClientDisconnected);
            Server.ServerStopping += new EventHandler(Server_ServerStopping);
        }


    }
}
